package cn.com.connected.common.config.jackson.serialize;//package com.ngkc.base.config.json.jackson.serialize;

import cn.com.connected.base.annotations.Dict;
import cn.com.connected.base.annotations.DictDb;
import cn.com.connected.base.annotations.Img;
import cn.com.connected.common.config.SpringApplicationContext;
import cn.com.connected.common.config.oss.AliyunOssProperties;
import cn.com.connected.common.emnus.DictDbType;
import cn.com.connected.common.utils.DataFormatUtils;
import cn.com.connected.vo.Dicts;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 注意：此处是针对某一个字段的，和在class上面直接加的不同
 * sms自己认为这里只是修改某个属性本身的值，而可以修改类本身的值，也就是可以额外追加字段，二不局限在属性本身值
 * 问题是，这儿和冲突 如果存在，此处就无法起作用了，正在找寻答案
 * @author
 * @Date 2019-06-16 15:32:25
 */

@Slf4j
public class MyBeanSerializerModifier extends BeanSerializerModifier {

    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    public boolean switchDict = true;

    public MyBeanSerializerModifier() {
    }

    private final static DecimalFormat df = new DecimalFormat("0.00");




    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
                                                     List<BeanPropertyWriter> beanProperties) {
        for (int i = 0; i < beanProperties.size(); i++) {

            BeanPropertyWriter writer = beanProperties.get(i);

            //字典翻译
            writer.assignSerializer(new DataJsonSerializer(writer));

            if (isArrayType(writer)) {
                writer.assignNullSerializer(new NullArrayJsonSerializer());
            }
//            if (isNumberType(writer)) {
//                writer.assignNullSerializer(new NullNumberJsonSerializer());
//            }
//            if (isBooleanType(writer)) {
//                writer.assignNullSerializer(new NullBooleanJsonSerializer());
//            }
//            if (isStringType(writer)) {
//                writer.assignNullSerializer(new NullStringJsonSerializer());
//            }
            /* 防止Long精度丢失 */
//            if(isLongType(writer)){
//                 writer.assignSerializer(new ToStringSerializer());
//            }
        }
        return beanProperties;
    }

         private boolean isArrayType(BeanPropertyWriter writer) {
//                JavaType javaType = writer.getType();
//                return javaType.isArrayType() || javaType.isCollectionLikeType();
            Class<?> clazz = writer.getType().getRawClass();
            return clazz.isArray() || Collection.class.isAssignableFrom(clazz);
        }

        private boolean isStringType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return CharSequence.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz);
        }

        private boolean isNumberType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return Number.class.isAssignableFrom(clazz);
        }
        private boolean isDoubleType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return Double.class.isAssignableFrom(clazz);
        }

        private boolean isLongType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return Long.class.isAssignableFrom(clazz);
        }

        private boolean isBooleanType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return clazz.equals(Boolean.class);
        }
        private boolean isDateType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return clazz.equals(Date.class);
        }


   private class NullArrayJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
            if (o == null) {
                jsonGenerator.writeStartArray();
                jsonGenerator.writeEndArray();
            }
        }
    }

    private class NullStringJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(StringUtils.EMPTY);
        }
    }

    private class NullNumberJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeNumber(0);
        }
    }

    private class NullBooleanJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeBoolean(false);
        }
    }

    private class DataJsonSerializer extends JsonSerializer<Object> {
        BeanPropertyWriter writer;
        public DataJsonSerializer(BeanPropertyWriter writer) {
            this.writer = writer;
        }

        @Override
        public void serialize(Object fieldValue, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
            String url = request.getRequestURI();
            try{
                //这儿很奇怪要放在本类最上部，不能放下面
                if(isDateType(writer)){
                    JsonFormat dict = writer.getAnnotation(JsonFormat.class);
                    if (dict!=null){
                        jsonGenerator.writeString(DataFormatUtils.format((Date)fieldValue,dict.pattern()));
                    }else {
                        jsonGenerator.writeString(DataFormatUtils.format((Date)fieldValue));
                    }
                }else if(isDoubleType(writer)){
                    jsonGenerator.writeString(df.format(fieldValue));
                }else{
                    //**********************************
                    //sms不理解之处 此处 非常 非常奇怪 必须先写入原值 writeString 或者 writeObject 然后才能 writeObjectField 如果把 writeObject写在if后面，就异常了
                    //**********************************

                    //图片翻译 是 覆盖原值
                    Img img = writer.getAnnotation(Img.class);
                    if (img!=null && fieldValue!=null){
                        if (fieldValue instanceof String){
                            fieldValue = addDomain((String) fieldValue);
                        } else if (fieldValue instanceof List){
                            List<String> vs = (List<String>) fieldValue;
                            fieldValue = vs.stream().map(o->addDomain(o)).collect(Collectors.toList());
                        }
                    }

                    jsonGenerator.writeObject(fieldValue);
                }


                //开始翻译>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

                //feign接口不翻译 防止重复翻译造成不必要资源浪费

                if (antPathMatcher.match("/*/feign/**",url)){
                    return;
                }

                String fieldName = writer.getName();
                Dict dict = writer.getAnnotation(Dict.class);

                if (dict!=null){
                    String dictKey = dict.value();
                    String key = dict.key();
                    String valueName = "";
                    if (StringUtils.isNotEmpty(key)){
                        valueName = key;
                    }else{
                        valueName = fieldName+"Name";
                    }

                    if (fieldValue!=null && !fieldValue.equals("")){
                        if (fieldValue instanceof Number){
                            String name = Dicts.dict(dictKey,(Number)fieldValue);
                            jsonGenerator.writeObjectField(valueName, name);
                        }else if (fieldValue instanceof List){
                            String names = ((List<?>) fieldValue).stream().map(o->Dicts.dict(dictKey,(Number)o)).collect(Collectors.joining(","));
                            jsonGenerator.writeObjectField(valueName,names);
                        }
                    }else{
                        jsonGenerator.writeObjectField(valueName,null);
                    }

                }
/*
            if (img!=null && fieldValue!=null){
                String results;
                if (fieldValue instanceof String){
                    results = addDomain((String) fieldValue);
                    jsonGenerator.writeObjectField(fieldName+"Src",results);
                }

                if (fieldValue instanceof List){
                    List<String> vs = (List<String>) fieldValue;
                    vs = vs.stream().map(o->addDomain(o)).collect(Collectors.toList());
                    jsonGenerator.writeObjectField(fieldName+"Src",vs);
                }
            }*/

                DictDb dictDb = writer.getAnnotation(DictDb.class);
                if (dictDb!= null){
                    DictDbType dictDbType = dictDb.value();
                    String key = dictDb.key();

                    String valueName = "";
                    if (StringUtils.isNotEmpty(key)){
                        valueName = key;
                    }else{
                        valueName = fieldName + "Name";
                        valueName = valueName.replace("IdName","Name");
                    }
                    if (fieldValue!=null){
                        Object v = dictFromDb(fieldValue,dictDbType);
                        jsonGenerator.writeObjectField(valueName,v);
                    }else{
                        jsonGenerator.writeObjectField(valueName,null);
                    }
                }
            }catch (Exception e){
                log.error("序列化错误 {} {}",url,e.getMessage());
            }

        }
    }

    private String getUrl(String client){
        return "/"+client+"/**";
    }

    public static String addDomain(String source){
        if (StringUtils.isNotEmpty(source) && !source.startsWith("http")) {
            AliyunOssProperties aliyunOssProperties = SpringApplicationContext.getBean(AliyunOssProperties.beanName);

            source = ("/"+source).replaceAll("[/]+","/");
            source = aliyunOssProperties.getOpenAccessHost() + source;
        }
        return  source;
    }

    Object dictFromDb(Object v, DictDbType type){
        if (v == null){
            return null;
        }

        try {

        }catch (Exception e){
            //目前不太清楚为啥这里面抛了异常 成了局部异常 可以手动throw试试 所以限制一下返回null
            log.error("json serializer exception : ",e);
        }
        return null;
    }
}